分类
联系方式
  1. 新浪微博
  2. E-mail

Vaex 低内存占用 Pandas 替代

背景

对超大型数据集做数据分析的时候,会面临内存装不下的问题,大量精力会被数据拆分、分批处理牵扯。

Pandas 是 Python 下最常用的数据分析框架,由于它将数据全部加载到内存中,因此面临超大数据集会有装不下的问题。

Vaex 是一个 Pandas 替代库,通过采用内存映射、懒加载、引用传值、延迟计算的方式,能够大大节省内存,因此适合于对超大型数据集进行分析。

两大优势:

  1. 数据加载速度快
  2. 内存占用低

缺点:

  1. 普及度小
  2. 网上资料、教程少
  3. Vaex 不支持 Pandas 的全部功能

Vaex 还有一个好的特性是跟 Pandas Dataframe 灵活互转,对已有的 Pandas 能实现更好的兼容。

内存节省

Vaex 为什么能节省内存?主要有以下原因。

内存映射

Vaex 以二进制格式存储数据(HDF5、Apache Arror、Apache Parquet),在加载数据的时候,它只是对数据格式进行内存映射,并没有加载内存数据。

因此速度非常快,同时内存开销很小。

懒加载

在 Vaex 中,数据只有在真正需要的时候才会被加载到内存中。

引用传值

在对 DataFrame 进行过滤的时候,并不会复制一个新的 DataFrame,而是在老的 DataFrame 之上对引用做过滤,从而节省大量内存。

延迟计算

在 Vaex 中有虚拟列的概念,即基于已有的列通过计算得到的新列,这些新列中的数据也是延迟计算的,只有在用到的时候,才会进行实际计算。

问题

describe 的性能?

describe(算最大、最小、均值、标准差)的时候,需要对数据进行遍历。

Vaex 能够实现以有限内存,对远大于内存的数据集计算整体信息。

由此可以看出,遍历数据的时候,数据在内存中不是常驻的。

排序是否要加载所有数据?

我觉得,应该遍历一边数据,然后按照排序后的建立索引。

实际效果

到底能省多少内存?

《python vaex处理大数据集》做了一个实验,vaex 内存占用只有 pandas 的 1/10。

文中还提到了一个 chunk 特性,能将大数据集拆分保存,这跟我需要的分片场景是十分像的,我如果能将分片按照 chunk 格式保存,加载的时候既不需要手动 concat 了。

扩展生态

vaex 有一系列扩展生态:

框架 说明
vaex-core 核心库
vaex-hdf5 hdf5 支持
vaex-arrow Apache Arrow 支持
vaex-viz 基于 Matplotlib 的可视化
vaex-jupyter jupyter 小部件
vaex-server 作为 Server,支持远程访问数据表
vaex-distributed 分布式运算
vaex-qt Qt 支持
vaex meta 包,包含以上库
vaex-ml 机器学习

数据导入

NumPy 数据变 DataFrame

import vaex
import numpy as np
x = np.arange(5)
y = x**2
df = vaex.from_arrays(x=x, y=y)

CSV 变 DataFrame

import vaex
for i, df in enumerate(vaex.from_csv('taxi.csv', chunk_size=100_000)):
    df = df[df.passenger_count < 6]
    df.export_hdf5(f'taxi_{i:02}.hdf5')

这里面的 chunk size 引起了我的注意,好像 hdf5 也支持 chunk。

Pandas DataFrame 变 Vaex's

import vaex, pandas as pd
df_pandas = pd.from_csv('test.csv')
df = vaex.from_pandas(df_pandas)

导入 S3 数据

nyctaxi = vaex.open('s3://vaex/taxi/yellow_taxi_2009_2015_f32.hdf5?anon=true')
nyctaxi.head(5)

其中,数据也是懒加载的,只会下载必要部分,并缓存在本地。

数据导出

DataFrame.to_pandas_df

DataFrame.export_hdf5

DataFrame

Vaex 的核心也是 DataFrame,Vaex 的 DataFrame 比 Pandas 的更加高效。

特性:

  • 在 Vaex 的表达式系统中,df.xdf['x']df.col.x 都是表达式。
  • 列和表达式都是懒加载的,如 df.x * np.sin(df.y) 什么也不干,直到实际访问结果
  • 虚拟列都是懒加载的,如 df['r'] = df.x/df.y
  • 对数据进行筛选:df.select(df.x < 0)
  • 对数据进行过滤:df_negative = df[df.x < 0]

示例数据集

import vaex
df = vaex.example()

示例数据集,行数大于 30w,有 9 个列

列访问

访问数据集中的一列:

df.x  # df.col.x or df['x'] are equivalent

返回值不是一个 Numpy 数组,而是一个 Expression。 通过 .values 将数据加载到内存中,获取到数组:

df.x.values

表达式运算

队列进行表达式运算:

import numpy as np
np.sqrt(df.x**2 + df.y**2 + df.z**2)

得到的还是一个表达式:Expression,并没有实际运算结果

虚拟列(Virtual Columns)

使用表达式 Expression 作为一个列,惰性求值,不用的话不占内存。

df['r'] = np.sqrt(df.x**2 + df.y**2 + df.z**2)

其中:r 就是一个虚拟列。

选择和过滤

条件选择

将条件选择实体化,拆分成选择和计算两个过程:

df.select(df.x < 0)
df.evaluate(df.x, selection=True)

这适合于数据集动态变化的情况,方便基于同一个选择求值。

过滤

跟 Pandas 比较像:

# 条件筛选
df_negative = df[df.x < 0]
# 获取指定列
df_negative[['x', 'y', 'z', 'r']]

统计运算

# 数据个数
df.count()
# 均值
df.mean(df.x)
# 选择部分的均值
df.mean(df.x, selection=True)

频数

统计在 limits 区间内的频数分布,shape 是分多少个格子:

counts_x = df.count(binby=df.x, limits=[-10, 10], shape=64)

使用 matplotlib 绘制图像:

import matplotlib.pylab as plt
plt.plot(np.linspace(-10, 10, 64), counts_x)
plt.show()

二维频数分布:

xycounts = df.count(
    binby=[df.x, df.y], 
    limits=[[-10, 10], [-10, 20]], 
    shape=(64, 128))

二维绘图:

plt.imshow(xycounts.T, origin='lower', extent=[-10, 10, -10, 20])
plt.show()

网络资源

手把手教你如何用 Python和Vaex 在笔记本上分析 100GB 数据

  • 伦敦出租车数据集
  • 提到怎么对 NumPy 进行加速的多种手段

Vaex真香!几秒钟就能处理数十亿行数据,比Pandas、Dask更好用

vaex读取和处理大型文件的方法

  • 给出了一个对期货螺纹钢的实例
  • apply 方法跟 Pandas 有点不一样、

vaex的使用

  • 一些使用的实例
  • 总结了 vaex 的常用使用方法
  • 格式有点乱,整体还是挺全的

Vaex处理大数据的Python库

有比Pandas更好的替代吗?对比Vaex,Dask,PySpark,Modin 和Julia

官网